﻿using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace SLServer
{
	internal class GameLogic
	{

		public static int[,] newMap = new int[15, 15];
		public static int[,] mapStatus = new int[15, 15];
		public static int[,] mapBoom = new int[15, 15];
		public static int[,] mapOpened = new int[15, 15];
		public static int boomCount = 0;
		public static bool firstTap = true;
		public static int opened = 0;

		public struct PointNum
		{
			public int point { get; set; }
			public int num { get; set; }
		}

		public GameLogic()
		{

		}

		public void doDebug(string msg, bool flag = true, int type = 0)
		{
			string a = "";
			switch (type)
			{
				case 0: a = "[DEBUG] "; break;
				case 1: a = "[INFO ] "; break;
				case 2: a = "[ERROR] "; break;
				case 3: a = "[UKNOW] "; break;
				default:break;
			}
			string nmsg = a + msg;
			Console.WriteLine(nmsg);
		}
		/// <summary>
		/// 初始化雷区
		/// </summary>
		/// <param name="boomNum">预设地雷数量</param>
		/// <param name="cx">玩家第一次点击的位置</param>
		/// <param name="cy">玩家第一次点击的位置</param>
		/// <returns></returns>
		public bool InitMapBoom(int boomNum, int cx, int cy)
		{
			boomCount = boomNum;
			//预设地雷数量高于单元格数或等于单元格数则生成失败
			if (boomNum >= 15 * 15)
			{
				return false;
			}
			doDebug("生成雷图");

			Random rd = new Random();
			//随机生成预设数量个数字
			for (int i = 0; i < boomNum; i++)
			{
				bool hasSet = false;
				while (hasSet == false)
				{
					int boom = rd.Next(0, 15 * 15);
					int x = (int)(boom / 15);
					int y = boom % 15;

					//如果生成的地雷在第一次点击的位置，则放弃设置该位置为地雷
					if (x == cx && y == cy)
						continue;
					//如果已经生成了地雷则放弃本轮生成
					if (mapBoom[x, y] == 9)
						continue;

					//通过判断，将设置成功标志加入

					hasSet = true;
					for (int xx = 0; xx < 3; xx++)
					{
						if (x - 1 + xx < 0 || x - 1 + xx >= 15) continue;
						for (int yy = 0; yy < 3; yy++)
						{
							int nx = x - 1 + xx;
							int ny = y - 1 + yy;
							if (y - 1 + yy < 0 || y - 1 + yy >= 15) continue;
							if (xx == 1 && yy == 1)
							{
								//将当前点设置为地雷
								mapBoom[nx, ny] = 9;
							}
							if (mapBoom[nx, ny] != 9)
							{
								//周围的单元格除雷区外全部数值+1
								mapBoom[nx, ny] += 1;
							}
						}
					}
				}
			}
			doDebug("生成雷图完毕");
			return true;
		}

		public List<int>[] ClickInsert(List<int>[] list, int x, int y, int num)
		{
			int point = 15 * x + y;
			if (list[num] == null)
			{
				list[num] = new List<int>();
			}
			list[num].Add(15*x+y);
			return list;
		}

		public int CheckWinOrDie(int x, int y, List<int>[] list)
		{
			//不再扫雷范围直接返回
			if (x >= 15 || x < 0 || y >= 15 || y < 0) return 1;
			//将扫描范围设置为开启
			mapOpened[x, y] = mapBoom[x, y];
			//关闭当前位置的可点击
			//修改单元格颜色
			//增加一个点开数量
			opened += 1;
			//如果点到雷区返回0 游戏结束
			ClickInsert(list, x, y, mapBoom[x, y]);
			if (mapBoom[x, y] == 9)
			{
				//mapBD[x, y].Text = "囧";
				return 0;
			}
			if (mapBoom[x, y] == 0)
			{
				//点到0（周围没有雷）就递归调用自己扫描周围的八个格子
				for (int xx = 0; xx < 3; xx++)
				{
					for (int yy = 0; yy < 3; yy++)
					{
						int newX = x - 1 + xx;
						int newY = y - 1 + yy;
						if ((newX != x || newY != y) && newX >= 0 && newY >= 0 && newX < 15 && newY < 15)
						{
							if (mapOpened[newX, newY] != -1)
							{
								continue;
							}
							CheckWinOrDie(newX, newY, list);
						}
					}
				}
			}
			if (15 * 15 - boomCount == opened)
			{
				return 2;
			}
			return 1;
		}

		public void GameOver(bool win)
		{
			bool status = false;
			if (win)
			{
				status = true;
			}
			else
			{
				Thread brd2 = new Thread(SendMsgToAll);
				JObject jo2 = JObject.FromObject(new { action = "Server_ShowAllBlock", data = mapBoom });
				brd2.Start(jo2.ToString());
			}
			JObject jo = JObject.FromObject(new { action = "Server_RoundResult",status=status });
			Thread brd = new Thread(SendMsgToAll);
			brd.Start(jo.ToString());
			doDebug("游戏结束");
			DoMyInit();
		}

		public void DoMyInit()
		{
			for(int i = 0; i < 15; i++)
			{
				for(int j = 0; j < 15; j++)
				{
					mapOpened[i, j] = -1;
				}
			}
			doDebug("游戏开始标志发送");
			
			newMap = new int[15, 15];
			mapStatus = newMap;
			mapBoom = newMap;
			//mapOpened = newMap;
			boomCount = 0;
			firstTap = true;
			opened = 0;
			JObject jo = JObject.FromObject(new { action = "Server_StartNew" });
			Thread brd = new Thread(SendMsgToAll);
			brd.Start(jo.ToString());
		}

		public void EnterRoom(Socket client)
		{
			Thread brd = new Thread(SendMsgToSelf);
			brd.Start(client);
		}


		public void DoClick(int nx, int ny)
		{
			string[] results = { };
			if (firstTap)
			{
				DoMyInit();
				InitMapBoom(15, nx, ny);
				string boom = "\n";
				for (int ii = 0; ii < 15; ii++)
				{
					for (int jj = 0; jj < 15; jj++)
					{
						boom += (" " + mapBoom[ii, jj]);
					}
					boom += "\n";
				}
				doDebug(boom, false);
				firstTap = false;
			}
			List<int>[] list = new List<int>[10];
			int ret = CheckWinOrDie(nx, ny, list);
			doDebug("点击");
			if(ret != 0 && list != null && list.Length > 0)
			{
				doDebug("返回点击结果");
				JObject res = JObject.FromObject(new { action="Server_ClickResult", pointNum = list });
				Thread brd = new Thread(SendMsgToAll);
				brd.Start(res.ToString());
			}
			else
			{
				doDebug("游戏结束，不发送click_result");
			}
			//
			if (ret == 0)
			{
				GameOver(false);
			}
			if (ret == 2)
			{
				GameOver(true);
			}
			//*/
		}

		public void CheckMsg(Socket client, string msg)
		{
			JObject jo = (JObject)JsonConvert.DeserializeObject(msg);
			string action = jo["action"].ToString();
			doDebug("消息处理:" + action);
			switch (action)
			{
				case "Click_Scanner":
					try
					{
						int nx = (int)jo["cx"];
						int ny = (int)jo["cy"];
						DoClick(nx, ny);
					}
					catch
					{
						DoError(10001);
					}
					break;
				default:
					break;
			}
		}

		public void DoError(int ecode)
		{
			Thread brd = new Thread(SendMsgToAll);
			brd.Start("发生错误！");
		}

		private static void SendMsgToSelf(Object client)
		{
			Socket cl = client as Socket;
			string m = "";
			if (firstTap == true)
			{
				JObject jo = JObject.FromObject(new { action = "Server_StartNew" });
				m = jo.ToString();
			}
			else
			{
				JObject jo = JObject.FromObject(new { action = "Server_UpdateGame", data = mapOpened });
				m = jo.ToString();
			}

			List<Socket> allClient = Program.allClient;

			m = m.Replace("\t", "").Replace("\n", "").Replace("\r", "").Replace(" ","");
			int len = m.Length;
			m = len.ToString().PadLeft(8, '0') + m;
			Console.WriteLine(m);

			if (cl != null && cl.Connected)
			{
				try
				{
					string sendMessage = m;
					cl.Send(Encoding.UTF8.GetBytes(sendMessage));
				}
				catch (Exception ex)
				{
					Console.WriteLine(ex.Message);
					if (cl != null && cl.Connected)
					{
						cl.Shutdown(SocketShutdown.Both);
						cl.Close();
					}
					if (allClient.Contains(cl) != true)
					{
						allClient.Remove(cl);
					}
				}
			}
			else
			{
				allClient.Remove(cl);
			}
		}

		private static void SendMsgToAll(Object omsg)
		{
			string m = omsg as string;
			m = m.Replace("\t", "").Replace("\n", "").Replace("\r", "").Replace(" ","");
			int len = m.Length;
			m = len.ToString().PadLeft(8, '0') + m;
			Console.WriteLine(m);

			List<Socket> allClient = Program.allClient;
			for (int i = 0; i < allClient.Count; i++)
			{
				Socket item = allClient[i];
				if (item != null && item.Connected)
				{
					try
					{
						string sendMessage = m;
						item.Send(Encoding.UTF8.GetBytes(sendMessage));
					}
					catch (Exception ex)
					{
						Console.WriteLine(ex.Message);
						if (item != null && item.Connected)
						{
							item.Shutdown(SocketShutdown.Both);
							item.Close();
						}
						if (allClient.Contains(item) != true)
						{
							allClient.Remove(item);
						}
					}
				}
				else
				{
					allClient.Remove(item);
				}
			}
		}
	}
}